package com.ynkbny.aspect;

import com.ynkbny.domain.TaskLog;
import com.ynkbny.service.TaskLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.quartz.impl.JobExecutionContextImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Arrays;

@Aspect
@Component
@Slf4j
public class TaskOperationLogAspect {

    private static Logger logger = LoggerFactory.getLogger(TaskOperationLogAspect.class);

    @Resource
    private TaskLogService taskLogService;

    /**
     * 以自定义 @TaskOperationLog 注解为切点，凡是添加 @TaskOperationLog 的方法，都会执行环绕中的代码
     */
    @Pointcut("@annotation(com.ynkbny.aspect.TaskOperationLog)")
    public void apiOperationLog() {
    }

    /**
     * 环绕
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("apiOperationLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        // 开始时间
        LocalDateTime start = LocalDateTime.now();
        // 类名
        String className = "";
        // 任务分组
        String taskGroup = "";
        // 入参
        String param = "";
        // 功能描述
        String description = "";
        TaskLog taskLog = null;
        try {

            // 获取类名
            className = joinPoint.getTarget().getClass().getSimpleName();

            // 请求入参
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                Object invokeParam = ((JobExecutionContextImpl) args[0]).getTrigger().getJobDataMap().get("invokeParam");
                if (invokeParam != null) {
                    param = invokeParam.toString();
                }
                taskGroup = ((JobExecutionContextImpl) args[0]).getTrigger().getJobKey().getGroup();
            }

            // 功能描述信息
            description = getApiOperationLogDescription(joinPoint);

            // 执行切点方法
            logger.info("定时任务开始执行---------------");
            Object result = joinPoint.proceed();
            logger.info("定时任务执行结束---------------");

            // 结束时间
            LocalDateTime end = LocalDateTime.now();

            // 计算耗时
            long duration = Duration.between(start, end).toMillis();

            taskLog = TaskLog.builder()
                    .taskName(description)
                    .taskGroup(taskGroup)
                    .invokeTarget(className)
                    .invokeParam(param)
                    .taskMessage("执行成功！ 总共耗时：" + duration + "毫秒")
                    .exceptionInfo(null)
                    .stopTime(end)
                    .status("0")
                    .startTime(start)
                    .build();
            return result;
        } catch (Throwable t) {
            logger.info("定时任务执行结束---------------");
            // 结束时间
            LocalDateTime end = LocalDateTime.now();
            // 计算耗时
            long duration = Duration.between(start, end).toMillis();

            taskLog = TaskLog.builder()
                    .taskName(description)
                    .taskGroup(taskGroup)
                    .invokeTarget(className)
                    .invokeParam(param)
                    .taskMessage("执行失败！ 总共耗时：" + duration + "毫秒")
                    .exceptionInfo(t.fillInStackTrace().toString() + Arrays.toString(t.getStackTrace()))
                    .stopTime(end)
                    .status("1")
                    .startTime(start)
                    .build();
            return null;
        } finally {
            if (taskLog != null) {
                logger.info("定时任务：{}", taskLog);
            }
            // 写入日志
            taskLogService.save(taskLog);
        }

    }

    /**
     * 获取注解的描述信息
     *
     * @param joinPoint
     * @return
     */
    private String getApiOperationLogDescription(ProceedingJoinPoint joinPoint) {
        // 1. 从 ProceedingJoinPoint 获取 MethodSignature
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        // 2. 使用 MethodSignature 获取当前被注解的 Method
        Method method = signature.getMethod();

        // 3. 从 Method 中提取 LogExecution 注解
        TaskOperationLog taskOperationLog = method.getAnnotation(TaskOperationLog.class);

        // 4. 从 LogExecution 注解中获取 description 属性
        return taskOperationLog.description();
    }

}